package cn.csust.net2.manager.server.remote;
import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import cn.csust.net2.manager.server.security.AuthCurrentUser;
import cn.csust.net2.manager.server.service.impl.BaseServiceImpl;
import cn.csust.net2.manager.shared.po.User;

import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
/**
 * 
 * @author 韩忠金
 *	将GWT的客户端远程调用绑定在特定的BEAN 上
 *  这样的处理目的去掉SERVLET层的调用去掉WEB一层
 *
 *
 *
 */
public class RemoteBeanServlet extends RemoteServiceServlet {
	/**
	 * 进行序列化的标识
	 */
	private static final long serialVersionUID = 1L;
	private static final Logger LOG = Logger.getLogger(RemoteBeanServlet.class);
	/**
	 * 日志
	 */
	@Override
	public void init() {
		if (LOG.isDebugEnabled()) {
			LOG.debug("GWT远程绑定BEAN初始完成");
		}
		if(BeanUtils.applicationContext==null)
			BeanUtils.applicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	}
	/**
	 * 复写远程的调用句柄的handler，送给RPC进行反射调用和序列化
	 */
	@Override
	public String processCall(String payload) throws SerializationException {
		try {
			//获得处理远程请求的对象
			Object handler = getBean(getThreadLocalRequest());
			//解析
			RPCRequest rpcRequest = RPC.decodeRequest(payload, handler.getClass(), this);
			//序列化
			onAfterRequestDeserialized(rpcRequest);
			if (LOG.isDebugEnabled()) {
				LOG.debug("Invoking " + handler.getClass().getName() + "." + rpcRequest.getMethod().getName());
			}
			return RPC.invokeAndEncodeResponse(handler, rpcRequest.getMethod(), rpcRequest.getParameters(), rpcRequest
					.getSerializationPolicy());
		} catch (IncompatibleRemoteServiceException ex) {
			log("An IncompatibleRemoteServiceException was thrown while processing this call.", ex);
			return RPC.encodeResponseForFailure(null, ex);
		}
	}

	/**
	 * 
	 * 
	 * @param request
	 * @return handler bean
	 */
	protected Object getBean(HttpServletRequest request) {
		String service = getService(request);
		Object bean = getBean(service,request);
		if (!(bean instanceof RemoteService)) {
			throw new IllegalArgumentException("Spring bean is not a GWT RemoteService: " + service + " (" + bean + ")");
		}
		if (LOG.isDebugEnabled()) {
			LOG.debug("Bean for service " + service + " is " + bean);
		}
		return bean;
	}

	/**
	 * 解析URL
	 * 
	 * @param request
	 * @return bean name
	 */
	protected String getService(HttpServletRequest request) {
		String url = request.getRequestURI();
		String service = url.substring(url.lastIndexOf("/") + 1);
		if (LOG.isDebugEnabled()) {
			LOG.debug("Service for URL " + url + " is " + service);
		}
		return service;
	}

	/**
	 * 在当前的CONTEXT中查找BREAN
	 * application context.
	 * 
	 * @param name
	 *            bean name
	 * @return the bean
	 */
	protected Object getBean(String name,HttpServletRequest request) {
		WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		if (applicationContext == null) {
			throw new IllegalStateException("No Spring web application context found");
		}
		if (!applicationContext.containsBean(name)) {
			{
				throw new IllegalArgumentException("Spring bean not found: " + name);
			}
		}
		/**
		 * 每次请求时候都将当前的USER绑定到线程变量中
		 */
		User user = (User)request.getSession().getAttribute(BaseServiceImpl.USER_SYS);
		AuthCurrentUser.set(user);
		//将HTTP的请求传递给调用的service
		if(applicationContext.getBean(name) instanceof RemoveServiceBean){
			RemoveServiceBean service = (RemoveServiceBean) applicationContext.getBean(name);
			service.loadRequest(request);
			return service;
		}
		return applicationContext.getBean(name);
	}
}
